package wiiusej.apptest;

import java.io.IOException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import java.util.Vector;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import wiiusej.*;
import wiiusej.apptest.activityTasks.ComClient;
import wiiusej.apptest.activityTasks.EnergyTransmisionTask;

import wiiusej.apptest.activityTasks.RealTimeFeedback;
import wiiusej.apptest.activityTasks.Spell;
import wiiusej.apptest.activityTasks.SpellGameTask;
import wiiusej.apptest.generalTasks.ActivateSpeaker;
import wiiusej.apptest.generalTasks.ActiveSound;
import wiiusej.apptest.generalTasks.EnableMotionTask;
import wiiusej.apptest.generalTasks.OnMotionSensingTask;
import wiiusej.apptest.generalTasks.VolumeSpeakerTask;
import wiiusej.apptest.softwareControllers.SoftwareController;
import wiiusej.apptest.softwareControllers.SoftwareControllerImpl;
import wiiusej.apptest.statistics.AccelerationPaquet;
import wiiusej.apptest.statistics.AccelerationPaquetList;
import wiiusej.apptest.statistics.PacketController;
import wiiusej.apptest.statistics.PacketManager;
import wiiusej.apptest.statistics.PacketWraped;
import wiiusej.apptest.statistics.performance.ClassTrackName;
import wiiusej.apptest.statistics.performance.PerformanceUnit;
import wiiusej.apptest.statistics.performance.Track;
import wiiusej.apptest.statistics.performance.TrackType;
import wiiusej.hardwareControllers.HardwareManagerImpl;
import wiiusej.hardwareControllers.PermanenSoundController;
import wiiusej.hardwareControllers.PermanentController;
import wiiusej.hardwareControllers.PermanentLEDContoller;
import wiiusej.hardwareControllers.PermanentRumbleController;

import wiiusej.values.RawAcceleration;
import wiiusej.wiiusejevents.physicalevents.ExpansionEvent;
import wiiusej.wiiusejevents.physicalevents.IREvent;
import wiiusej.wiiusejevents.physicalevents.MotionSensingEvent;
import wiiusej.wiiusejevents.physicalevents.WiimoteButtonsEvent;
import wiiusej.wiiusejevents.utils.WiimoteBasicListener;
import wiiusej.wiiusejevents.utils.WiimoteListener;
import wiiusej.wiiusejevents.wiiuseapievents.ClassicControllerInsertedEvent;
import wiiusej.wiiusejevents.wiiuseapievents.ClassicControllerRemovedEvent;
import wiiusej.wiiusejevents.wiiuseapievents.DisconnectionEvent;
import wiiusej.wiiusejevents.wiiuseapievents.GuitarHeroInsertedEvent;
import wiiusej.wiiusejevents.wiiuseapievents.GuitarHeroRemovedEvent;
import wiiusej.wiiusejevents.wiiuseapievents.NunchukInsertedEvent;
import wiiusej.wiiusejevents.wiiuseapievents.NunchukRemovedEvent;
import wiiusej.wiiusejevents.wiiuseapievents.StatusEvent;
import wiiusej.wiiusejevents.wiiuseapievents.WiimoteEvent;

import org.apache.log4j.Category;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.spi.RootLogger;


public class WiimoteEventListener implements WiimoteBasicListener{
	
	//Logger
	private static Logger fileLogger;
		
	//Logger URL
	private static final String fileURL = "src/test/java/wiiusej/resources/logs/tasks/WiimoteListener";
	
	//class name
	private final ClassTrackName className = ClassTrackName.LISTENER;
	
	
	//Machine States
	private AtomicBoolean speakerStatus = new AtomicBoolean(false);
	private AtomicBoolean rumbleStatus = new AtomicBoolean(true);
	
	Boolean motionButtonState = false;
	Integer captures = 0;
	
	//Concurrent Main Class
	protected static ExecutorService exe;
	
	//Wiimote object representation
	private Wiimote wiimote;
	
	//Energy Controller
	private AtomicInteger energyCharged = new AtomicInteger();
	
	List<AccelerationPaquet> eventList
    	= Collections.synchronizedList(new ArrayList<AccelerationPaquet>());
	
	
	private PacketWraped myPackets;
	private Timestamp lastTime;
	
	Vector<AccelerationPaquet> myEventVector;
	
	protected long randomID;
	
	AccelerationPaquetList accPacketList;
	
	//COM QUEUES
	private BlockingQueue<Vector<AccelerationPaquet>> realTimeQueue 
		= new LinkedBlockingDeque<Vector<AccelerationPaquet>>();
	private BlockingQueue<Integer> gameToEnergy = new LinkedBlockingDeque<Integer>();
	private BlockingQueue<Integer> realTimeToGameController = new LinkedBlockingDeque<Integer>();
	private BlockingQueue<Spell> eneergyToComClient;
	private BlockingQueue<Integer> toRumbleQ = 
		new LinkedBlockingQueue<Integer>();
	private BlockingQueue<Integer> fromRealToSoundQ = 
		new LinkedBlockingQueue<Integer>();
	private BlockingQueue<Integer> toLEDQ = 
		new LinkedBlockingQueue<Integer>();
	private ConcurrentLinkedQueue<Integer> fromEnergyToGameQ = 
		new ConcurrentLinkedQueue<Integer>();
	private BlockingQueue<Track> toPerformanceController;
	
	
	//PacketController
	private PacketController pController;
		
	
	private HardwareManagerImpl hardwareManager; 
		
		
	public WiimoteEventListener(Wiimote wiimote, ExecutorService exe, 
			ConcurrentLinkedQueue<AccelerationPaquetList> syncQueue, BlockingQueue<Spell> eneergyToComClient, 
			BlockingQueue<Track> fromSystem) {
		
		super();		
		
		this.wiimote = wiimote;
		this.exe = exe;
		
		//COM QUEUES
		this.eneergyToComClient = eneergyToComClient;
		this.toPerformanceController = fromSystem;		
		
		
		accPacketList = new AccelerationPaquetList(wiimote.getId());				
		myPackets = new PacketWraped(accPacketList); 
		
		this.pController = new PacketController(this.wiimote.getId(), 
				randomID, toPerformanceController);
		/*
		PropertyConfigurator.configure("log4j.properties");
		fileLogger = Logger.getLogger("speaker");*/
		
		Random random = new Random(System.currentTimeMillis());
		this.randomID = random.nextLong();
		
		
		
		PropertyConfigurator.configure("log4j.properties");
		this.fileLogger = Logger.getLogger("wiimoteListener");
				
		
		
		//register this listener in the Wiimote's listener list
		registerListeners();	
		
		//Speaker, rumbler and LED controllers
		initHardwareControllers();
		
		//System and Game Controllers
		initSoftwareComponents();
		
		//Turn on the acceleration packets capture
		activateMotionSensing();
		
		
		
	}	
	
	private void initHardwareControllers(){		
		hardwareManager = new HardwareManagerImpl(wiimote, fromRealToSoundQ, toRumbleQ, toLEDQ);
		exe.execute(hardwareManager);		
	}
	
	
	private void initSoftwareComponents(){	
		SoftwareController gameManager = new SoftwareControllerImpl(wiimote, realTimeQueue, realTimeToGameController, fromRealToSoundQ, gameToEnergy, 
				toRumbleQ, fromEnergyToGameQ, eneergyToComClient, toLEDQ, toPerformanceController);		
		
	}	
	
	
	
	private void activateMotionSensing() {
		wiimote.activateMotionSensing();		
	}

	private void createDataChunks(MotionSensingEvent event, Timestamp packetTime){
		this.myPackets = pController.makeChunks(event, myPackets, packetTime);		
	}
	
	private void realTimeApp(MotionSensingEvent arg0){
		try {
			realTimeQueue.put((Vector<AccelerationPaquet>) myPackets.getMyEventVector().getPackets().clone());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}		
	}	
	
	private void sendToPerformanceConroller(MotionSensingEvent e){
		Timestamp time = new Timestamp(System.currentTimeMillis());		
		//PerformanceUnit unitReport = new PerformanceUnit(wiimote.getId(), e.getRefNumber(), time);
		Track track = new Track(className, time, e.getRefNumber(), wiimote.getId(), TrackType.ACCELERATION);
		
		try {
			toPerformanceController.put(track);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		
	}
	
	/***********************************************************
	//WIIMOTE LISTENER INTERFACE 
	************************************************************/
	
	public void registerListeners() {
		//We have to add our listener to the wiimote object
		System.out.println("Wiimote Listener - Registering in Wiimote List");
		wiimote.addWiiMoteEventListeners(this);		
	}
	
	//Button Listener - will act as a State Machine
	public void onButtonsEvent(WiimoteButtonsEvent e) {
	        
	    //ENABLE MOTION CAPTURING
	    if (e.isButtonAJustPressed()){	    	
	    	try {
				gameToEnergy.put(0);
			} catch (InterruptedException ex) {
				ex.printStackTrace();
			}	    	
	    }	    
	}
	
	//Motion Sensing Listener - depends on the state will capture motion measurements
	public void onMotionSensingEvent(MotionSensingEvent e, long packetRefNumber, Timestamp packetTime) {		
		
		SimpleDateFormat tsFormat = new SimpleDateFormat("mm:ss:SS");
		//long start = System.currentTimeMillis();
		e.setRefNumber(packetRefNumber);
		e.setTime(packetTime);
		
		sendToPerformanceConroller(e);
			
		fileLogger.info(" ");
		//fileLogger.debug("PacketTime: " + tsFormat.format(packetTime) + "#: " + e.getRefNumber());
		
		createDataChunks(e, packetTime);
		
		//fileLogger.debug("MypcketsSize" + myPackets.getMyEventVector().getPackets().size());
		//long end1 = System.currentTimeMillis();
		
		/*
		fileLogger.info(" PacketIN: " + tsFormat.format(start) + 
				" PacketAfterstats: " + tsFormat.format(end1) + " Difference: " + 
				(end1-start));
		*/
		
		//sendToPerformanceConroller(e);
		//syncQueue.offer(accPacketList);
		
		if(myPackets.getMyEventVector().getPackets().size() > 0){
			fileLogger.debug("LastPacketTime: " + 
					tsFormat.format(myPackets.getMyEventVector().getPackets().lastElement().getTime()) + 
					" Counter: " + myPackets.getMyEventVector().getPackets().lastElement().getCounter());
			realTimeApp(e);			
		}
				
    }	
	
	@Override
	public void onDisconnectionEvent(DisconnectionEvent e) {			
	}	

	@Override
	public void onStatusEvent(StatusEvent e) {		
	}

	
	@Override
	public String toString(){
		return Integer.toString(wiimote.getWiimoteId());
	}

	
	
	
}


